
# 2048 核心算法

import random

debug = False  # 运行模式
if debug:
    map_data = [
        [2,  8, 8, 2],
        [4, 16, 0, 2],
        [8,  2, 0, 0],
        [2,  8, 0, 0],
    ]
else:
    map_data = [
        [0, 0, 0, 0],
        [0, 0, 0, 0],
        [0, 0, 0, 0],
        [0, 0, 0, 0],
    ]


total_score = 0  # 用来保存得分的全局变量

def left_move_number(aline_list):
    '''将所有的数字靠左对齐，0放在右侧'''
    for _ in range(3):
        for i in range(3):
            if aline_list[i] == 0:
                aline_list[i] = aline_list[i+1]
                aline_list[i+1] = 0

def left_merge_number(aline_list):
    '''自左向右合并相邻的数据（两个值项等就合并）
    [2, 8, 8, 2] ----> [2, 16, 0, 2]
    [8, 8, 2, 2] ----> [16, 0, 4, 0]
    '''
    for i in range(3):
        if aline_list[i] == aline_list[i+1]:
            aline_list[i] *= 2
            # 将合并后的数值作为分数，记录再全局变量中
            global total_score
            total_score += aline_list[i]
            aline_list[i+1] = 0

# 核心算法1
def left_move_aline(aline_list):
    # 分三步走
    # 1. 第一步，全部左移不合并
    left_move_number(aline_list)
    # 2. 第二步，自左向右合并相邻的数据（两个值项等就合并）
    left_merge_number(aline_list)
    # 3. 第三步，全部左移不合并
    left_move_number(aline_list)

# 基本操作，
def left():
    '左移'
    for aline in map_data:
        left_move_aline(aline)

def right():
    '右移'
    for aline in map_data:
        aline.reverse()  # 反转镜像
        left_move_aline(aline)
        aline.reverse()  # 反转回来

def up():
    '''上移
    将每一列放入一个有四个元素的列表中， 再左移此列表，
    再放回原位则完成了一个列的上移
    '''
    # 循环定义列的索引 col
    for col in range(4):
        aline = [0, 0, 0, 0]  #
        # 1. 将 col 对应的列的数据放入 aline列表中
        for row in range(4):  # row是行索引
            aline[row] = map_data[row][col]
        # 2. 左移aline
        left_move_aline(aline)
        # 3. 将 aline中的数据返回到col列对应的位置
        for row in range(4):  # row是行索引
            map_data[row][col] = aline[row]

def down():
    '下移'
    # 1. 将 map_data 上下反转
    map_data.reverse()
    # 2. 执行上移操作
    up()
    # 3. 将 map_data 上下反转回来
    map_data.reverse()

def get_space_count():
    '''获取没有数字的单元格的个数'''
    count = 0
    for aline in map_data:
        count += aline.count(0)
    return count

def fill_a_random_number():
    '''
    此方法用于再地图的空白位置（值为0的位置）填充一个2或 4
    2的概率占 4/5, 4的概率占1/4
    '''
    # 获取空格的个数
    blank_count = get_space_count()
    # 如果空格个数为0，则无法放入数字,取消放置
    if blank_count == 0:
        return False
    random_list = [2, 2, 2, 2, 4]
    fill_number = random.choice(random_list)
    print('fill_number:', fill_number)
    # 随即生成一个要填充的位置，此位置为[0,blank_count) 范围内
    pos = random.randrange(0, blank_count)
    # 创建一个变量 offset , 此变量记录是第几个0的位置
    offset = 0  # 0代表第一个
    # 行列遍历 地图，当发现0时，来确定是否填充。
    for row in range(4):  # 行
        for col in range(4):  # 列
            if map_data[row][col] == 0:  # 遇到空白位置
                if pos == offset:
                    map_data[row][col] = fill_number  # 填充
                    return True
                offset += 1  # 取找下一个0的位置

def is_gameover():
    '''判断游戏是否结束，如果结束返回True, 否则返回False'''
    # 1. 如果有0存在，游戏还没有结束
    if get_space_count():
        return False
    # 2. 如果没有 0 存在
    # 2.1 如果用横向的两个值相同，说明可以合并,游戏没有结束
    for row in range(4):
        for col in range(3):
            if map_data[row][col] == map_data[row][col+1]:
                return False
    # 2.2 如果用纵向的两个值相同，说明可以合并,游戏没有结束
    for col in range(4):
        for row in range(3):
            if map_data[row][col] == map_data[row+1][col]:
                return False
    return True

def show_map():
    # 显示的得分:
    print('分数：', total_score)
    for aline in map_data:
        print('+------+------+------+------+')
        s = '| ' + ' | '.join([str(x).center(4) if x else '    ' for x in aline]) + ' |'
        print(s)
    print('+------+------+------+------+')


def main():
    while True:
        show_map()
        # 判断游戏是否结束
        if is_gameover():
            print('游戏结束')
            break
        s = input('请按键：a/s/d/w:')
        if s == 'a':
            left()
            fill_a_random_number()
        elif s == 'd':
            right()
            fill_a_random_number()
        elif s == 's':
            down()
            fill_a_random_number()
        elif s == 'w':
            up()
            fill_a_random_number()

#     map_data = [
#         [2,  8, 8, 2],
#         [4, 16, 0, 2],
#         [8,  2, 0, 0],
#         [2,  8, 0, 0],
#     ]

if __name__ == '__main__':
    main()
    # show_map()
    # fill_a_random_number()
    # show_map()
    # print(get_space_count())  # 5
    # line1 = [0, 0, 0, 2]
    # print(line1)
    # left_move_number(line1)
    # print(line1)  # [8, 0, 2， 0]
    # line2 = [8, 8, 2, 2]
    # print(line2)
    # left_move_aline(line2)
    # print(line2)
